package com.xiyuan.smartutils.reflect;

import com.xiyuan.smartutils.Types;

import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Field;
import java.lang.reflect.Type;

import static com.xiyuan.smartutils.constants.TypeConstants.*;
import static com.xiyuan.smartutils.reflect.Reflect.UNSAFE;

/**
 * 反射工具类#字段操作
 *
 * @author xiyuan-lgz 2025-03-13 @version v1.0.0 新建与整理
 */
@SuppressWarnings("all")
public class ReflectField {
    private Field field;
    private MethodHandle getter;
    private MethodHandle setter;
    private Class<?> type;
    private int typeHash = 0;
    private Object obj;
    private long offset;
    private boolean isPrimitive;
    private boolean isPrimitiveArray;
    
    private ReflectField() {
    }
    
    public ReflectField(Field field) {
        this.field = field;
        try {
            Reflect.setAccessible(field);
            offset = UNSAFE.objectFieldOffset(field);
            setter = MethodHandles.lookup().unreflectSetter(field);
            getter = MethodHandles.lookup().unreflectGetter(field);
            type = getter.type().returnType();
            typeHash = Types.getClassHashCode(type);
            
            isPrimitive = type.isPrimitive();
            isPrimitiveArray = Types.isPrimitiveArray(type);
        }
        catch (RuntimeException e) {
            throw e;
        }
        catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
    
    
    public long getOffset() {
        return offset;
    }
    
    /**
     * 绑定到对象上面 使得 调用时类似 obj.xxx
     *
     * @param obj
     * @return
     */
    public ReflectField bind(Object obj) {
        ReflectField rf = new ReflectField();
        rf.field = field;
        rf.setter = setter.bindTo(obj);
        rf.getter = getter.bindTo(obj);
        rf.type = type;
        rf.typeHash = typeHash;
        rf.obj = obj;
        rf.offset = offset;
        rf.isPrimitive = isPrimitive;
        rf.isPrimitiveArray = isPrimitiveArray;
        return rf;
    }
    
    public Field getField() {
        return field;
    }
    
    
    public void set(Object value) {
        // 基础类型并且 值 为 null 不设置
        if (type.isPrimitive() && value == null) {return;}
        
        try {
            if (obj != null) {
                this.setter.invoke(value);
            }
        }
        catch (RuntimeException e) {
            e.setStackTrace(initStack(e));
            throw e;
        }
        catch (Throwable e) {
            e.setStackTrace(initStack(e));
            IllegalArgumentException ex = new IllegalArgumentException(e);
            ex.setStackTrace(initStack(ex));
            throw ex;
        }
    }
    
    public void set(Object obj, Object value) {
        // 基础类型并且 值 为 null 不设置
        if (type.isPrimitive() && value == null) {return;}
        
        try {
            this.setter.invoke(obj, wrapperValue(value));
        }
        catch (RuntimeException e) {
            e.setStackTrace(initStack(e));
            throw e;
        }
        catch (Throwable e) {
            e.setStackTrace(initStack(e));
            IllegalArgumentException ex = new IllegalArgumentException(e);
            ex.setStackTrace(initStack(ex));
            throw ex;
        }
    }
    
    private <T> T wrapperValue(Object value) {
        
        switch (typeHash) {
            case INT_CODE:
            case SHORT_CODE:
            case LONG_CODE:
            case FLOAT_CODE:
            case DOUBLE_CODE:
            case BYTE_CODE:
            case CHAR_CODE:
                return (T) (value != null ? value : value);
            case BOOLEAN_CODE:
                return (T) (value != null ? value : false);
            
            default:
                return (T) obj;
        }
        
        
    }
    
    public Object get(Object obj) {
        try {
            return this.obj == null ? this.getter.invoke(obj) : this.getter.invoke();
        }
        catch (RuntimeException e) {
            e.printStackTrace();
            e.setStackTrace(initStack(e));
            throw e;
        }
        catch (Throwable e) {
            
            e.setStackTrace(initStack(e));
            IllegalArgumentException ex = new IllegalArgumentException(e);
            ex.setStackTrace(initStack(ex));
            throw ex;
        }
        
    }
    
    public Class<?> getDeclaringClass() {
        return field.getDeclaringClass();
    }
    
    public String getName() {
        return field.getName();
    }
    
    public int getModifiers() {
        return field.getModifiers();
    }
    
    public boolean isEnumConstant() {
        return field.isEnumConstant();
    }
    
    public Class<?> getType() {
        return field.getType();
    }
    
    public Type getGenericType() {
        return field.getGenericType();
    }
    
    public boolean equals(Object obj) {
        return field.equals(obj);
    }
    
    public int hashCode() {
        return field.hashCode();
    }
    
    public String toGenericString() {
        return field.toGenericString();
    }
    
    /**
     * 判断字段是否是合成的（synthetic）
     * 合成字段是Java编译器为了实现某些语言特性（如桥接方法、合成属性等）而自动生成的字段
     *
     * @return 如果字段是合成的，则返回true；否则返回false
     */
    public boolean isSynthetic() {
        // 调用field对象的isSynthetic方法，并返回其结果
        // field是一个对象，可能是某个类的实例，该类具有isSynthetic方法
        return field.isSynthetic();
    }
    
    /**
     * 判断字段是否可访问
     * 可访问性决定了字段是否可以被外部类或方法访问和修改
     *
     * @return 如果字段可访问，则返回true；否则返回false
     */
    public boolean isAccessible() {
        return field.isAccessible();
    }
    
    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        return field.getAnnotation(annotationClass);
    }
    
    public <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
        return field.getAnnotationsByType(annotationClass);
    }
    
    public Annotation[] getDeclaredAnnotations() {
        return field.getDeclaredAnnotations();
    }
    
    public AnnotatedType getAnnotatedType() {
        return field.getAnnotatedType();
    }
    
    
    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return field.isAnnotationPresent(annotationClass);
    }
    
    
    // 定义常量GETTER_POS和SETTER_POS，用于标识getter和setter方法的位置
    // 这些常量用于在堆栈跟踪中识别getter和setter方法
    private static final String GETTER_POS = ReflectField.class.getName() + "#get";
    private static final String SETTER_POS = ReflectField.class.getName() + "#set";
    
    /**
     * 初始化堆栈跟踪数组，用于获取调用getter或setter方法的调用者信息
     * 该方法通过过滤掉getter和setter方法之前的堆栈跟踪元素，来定位到调用这些方法的外部调用者
     *
     * @param e 一个Throwable对象，用于获取初始的堆栈跟踪信息
     * @return 过滤后的堆栈跟踪数组，包含调用getter或setter方法的调用者信息
     */
    private static StackTraceElement[] initStack(Throwable e) {
        StackTraceElement[] ls = e.getStackTrace();
        int i = 0;
        for (StackTraceElement l : ls) {
            i++;
            String k = l.getClassName() + "#" + l.getMethodName();
            if (GETTER_POS.equals(k) || SETTER_POS.equals(k)) {
                break;
            }
        }
        StackTraceElement[] target = new StackTraceElement[ls.length - i];
        System.arraycopy(ls, i, target, 0, target.length);
        return target;
    }
    
    /**
     * 重写toString方法，返回字段的字符串表示
     * 该方法通常用于调试和日志记录，提供字段的详细信息
     *
     * @return 字段的字符串表示
     */
    @Override
    public String toString() {
        return field.toString();
    }
    
    
    //*********************************************************************************************
    //*************************get & set **********************************************************
    //*********************************************************************************************
    
    /**
     * 设置 属性值 本方法 使用Unsafe 进行设置，请注意 值类型合法性
     *
     * @param obj
     * @param value
     */
    public void setValue(Object obj, Object value) {
        if (obj == null) return;
        switch (typeHash) {
            case INT_CODE:
                UNSAFE.putInt(obj, offset, value == null ? 0 : (int) value);
                break;
            case SHORT_CODE:
                UNSAFE.putShort(obj, offset, value == null ? 0 : (short) value);
                break;
            case LONG_CODE:
                UNSAFE.putLong(obj, offset, value == null ? 0 : (long) value);
                break;
            case FLOAT_CODE:
                UNSAFE.putFloat(obj, offset, value == null ? 0 : (float) value);
                break;
            case DOUBLE_CODE:
                UNSAFE.putDouble(obj, offset, value == null ? 0 : (double) value);
                break;
            case BYTE_CODE:
                UNSAFE.putByte(obj, offset, value == null ? 0 : (byte) value);
                break;
            case CHAR_CODE:
                UNSAFE.putChar(obj, offset, value == null ? 0 : (char) value);
                break;
            case BOOLEAN_CODE:
                UNSAFE.putBoolean(obj, offset, value == null ? false : (boolean) value);
                break;
            default:
                UNSAFE.putObject(obj, offset, value);
                break;
        }
        
    }
    
    /**
     * 获取 属性值 本方法 使用Unsafe 进行获取，请注意 值类型合法性验证
     *
     * @param obj
     * @param <T>
     * @return
     */
    public <T> T getValue(Object obj) {
        if (obj == null) return null;
        switch (typeHash) {
            case INT_CODE:
                return (T) (Integer) UNSAFE.getInt(obj, offset);
            case SHORT_CODE:
                return (T) (Short) UNSAFE.getShort(obj, offset);
            case LONG_CODE:
                return (T) (Long) UNSAFE.getLong(obj, offset);
            case FLOAT_CODE:
                return (T) (Float) UNSAFE.getFloat(obj, offset);
            case DOUBLE_CODE:
                return (T) (Double) UNSAFE.getDouble(obj, offset);
            case BYTE_CODE:
                return (T) (Byte) UNSAFE.getByte(obj, offset);
            case CHAR_CODE:
                return (T) (Character) UNSAFE.getChar(obj, offset);
            case BOOLEAN_CODE:
                return  (T) (Boolean) UNSAFE.getBoolean(obj, offset);
            default:
                return (T) UNSAFE.getObject(obj, offset);
        }
    }
    
    
    public static void setBoolean(Object obj, long offset, boolean value) {
        Reflect.setBoolean(obj, offset, value);
    }
    
    public static void setBoolean(Object obj, long offset, Boolean value) {
        Reflect.setObjectValue(obj, offset, value);
    }
    
    public static boolean getBoolean(Object obj, long offset) {
        return Reflect.getBoolean(obj, offset);
    }
    
    public static long getLong(Object obj, long offset) {
        return Reflect.getLong(obj, offset);
    }
    
    public static void setLong(Object obj, long offset, long value) {
        Reflect.setLong(obj, offset, value);
    }
    
    public static void setLong(Object obj, long offset, Long value) {
        Reflect.setObjectValue(obj, offset, value);
    }
    
    public static void setInt(Object obj, long offset, int value) {
        Reflect.setInt(obj, offset, value);
    }
    
    public static void setInt(Object obj, long offset, Integer value) {
        Reflect.setObjectValue(obj, offset, value);
    }
    
    public static int getInt(Object obj, long offset) {
        return Reflect.getInt(obj, offset);
    }
    
    
    public static void setByte(Object obj, long offset, byte value) {
        Reflect.setByte(obj, offset, value);
    }
    
    public static void setByte(Object obj, long offset, Byte value) {
        Reflect.setObjectValue(obj, offset, value);
    }
    
    public static byte getByte(Object obj, long offset) {
        return Reflect.getByte(obj, offset);
    }
    
    public static void setChar(Object obj, long offset, char value) {
        Reflect.setChar(obj, offset, value);
    }
    
    public static void setChar(Object obj, long offset, Character value) {
        Reflect.setObjectValue(obj, offset, value);
    }
    
    public static char getChar(Object obj, long offset) {
        return Reflect.getChar(obj, offset);
    }
    
    public static void setShort(Object obj, long offset, short value) {
        Reflect.setShort(obj, offset, value);
    }
    
    public static void setShort(Object obj, long offset, Short value) {
        Reflect.setObjectValue(obj, offset, value);
    }
    
    public static short getShort(Object obj, long offset) {
        return Reflect.getShort(obj, offset);
    }
    
    public static void setDouble(Object obj, long offset, double value) {
        Reflect.setDouble(obj, offset, value);
    }
    
    public static void setDouble(Object obj, long offset, Double value) {
        Reflect.setObjectValue(obj, offset, value);
    }
    
    public static double getDouble(Object obj, long offset) {
        return Reflect.getDouble(obj, offset);
    }
    
    public static void setFloat(Object obj, long offset, float value) {
        Reflect.setFloat(obj, offset, value);
    }
    
    public static void setFloat(Object obj, long offset, Float value) {
        Reflect.setObjectValue(obj, offset, value);
    }
    
    public static float getFloat(Object obj, long offset) {
        return Reflect.getFloat(obj, offset);
    }
}

